#include <stdint.h>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
#include <bitset>
#include <queue>

//#define DEBUG

using namespace std;


class BFMachine 
{
	std::vector<uint8_t> data;  
	std::string code;
	unsigned int data_ptr;
	unsigned int instruction_ptr;
	std::string istr;
	
	// 用来输出到机器外部的流，为'.'所用。
	std::ostream& OUTPUT_STREAM;
	std::string ostr;

	int interpret()
	{
		/*
		本函数参考知乎用户【谷雨同学】的答案：
		链接：https://www.zhihu.com/question/816782141/answer/4817413778
		*/
		auto code_size = code.size();
		auto data_size = data.size();

		for (instruction_ptr = 0, cycle_count = 0; 
			instruction_ptr < code_size; 
			instruction_ptr++, cycle_count++) {
	#ifdef DEBUG
			display();
	#endif
			switch (code[instruction_ptr]) {
			case '>':
				if (data_ptr < data_size) {
					++data_ptr; 
				} else {
					return -1;
				}
				break;
			case '<': 
				if (data_ptr > 0) {
					--data_ptr;
				} else {
					return -2;
				} 
				break;
			case '+': ++data[data_ptr]; break;
			case '-': --data[data_ptr]; break;
			case ',': 
				{
					char c;
					istr += data[data_ptr] = (INPUT_STREAM.get(c) ? c : 0);
				}
				break;
			case '.': 
				{
					char c = data[data_ptr];
					OUTPUT_STREAM.put(c);
					ostr += c;
				}
				break;
			case '[':
				if (data[data_ptr] == 0) {
					int loop = 1;
					while (loop > 0) {
						auto current_char = code[++instruction_ptr];
						if (current_char == '[')
							loop++;
						else if (current_char == ']')
							loop--;
					}
				}
				break;
			case ']':
				if (data[data_ptr]) {
					int loop = 1;
					while (loop > 0) {
						auto current_char = code[--instruction_ptr];
						if (current_char == '[')
						loop--;
						else if (current_char == ']')
						loop++;
					}
				}
				break;
			}
		} 
		return 0;
	}

	uint64_t cycle_count;
public:
	BFMachine(uint32_t n, std::ostream& OS=cout)	
		:data_ptr(0), instruction_ptr(0), data(n, 0), code(""), 
		INPUT_STREAM(), 
		OUTPUT_STREAM(OS),
		cycle_count(0) {}

	void eat (std::string s) {
		std::for_each(s.begin(),s.end(),
			[&](const char& c){
				switch(c) {
				case '>':
				case '<':
				case '+':
				case '-':
				case '.':
				case ',':
				case '[':
				case ']': code+=c;
				}
			}
		);
	}

	int end_eat() {
		int result = interpret();
		display();
		return result;
	}

	// 从机器外部获得输入用的流，为','所用。
	class: public std::istream
	{
		using QUEUE = queue<istream*>;
		QUEUE q;
	public:
		void push(istream* is_ptr) {
			q.push(is_ptr);
		}

		istream& get(char& c) {
			while(1) {
				if(q.empty()) {
					setstate(ios_base::eofbit);
					return *this;
				} else if (q.front()->get(c)){
					return *q.front();
				} else {
					q.pop();
				}
			}
		}
	} INPUT_STREAM;

	ostream& display(ostream& os=cout) {
		os << "## CYCLE " << this->cycle_count << " ##" << endl;
		{
			os <<"CODE: "<< endl;
			for (int i = 0; i <= this->code.size() ; ++i) {
				if (i == this->instruction_ptr) {
					os << '*' ;
					if (this->instruction_ptr == this->code.size()) {
						break;
					}
				} 
				os << char(this->code[i]);
			}
			os << endl;
		}
		{
			os <<"DATA: "<< endl;
			int i = 0;
			auto it = this->data.begin();
			for (; it != this->data.end() ; ++it, ++i) {
				os << ((i == this->data_ptr) ? '*':' ') << setfill('0') << setw(2) << hex << int(*it);
				if (0x21<=*it && *it<=0x7e)
					os << '\'' << char(*it) <<'\'';
			}
			os << endl;
		}
		os	<<"instruction_ptr: "<< endl
			<< dec << this->instruction_ptr <<endl
			<<"data_ptr: "<< endl
			<< dec << this->data_ptr  <<endl
			<< "INPUT:" <<endl
			<< this->istr << endl
			<< "OUTPUT:" << endl
			<< this->ostr <<endl;
		return os;
	}

	uint64_t cycles() const{
    	return cycle_count;
	}
};


// #define TEST
int main(int args, char** argv)
{
	BFMachine a_machine(4096);

#ifdef  TEST

	a_machine.eat(
		string(R"(
->+>+++>>+>++>+>+++>>+>++>>>+>+>+>++>+>>>>+++>+>>++>+>+++>>++>++>>+>>+>++>++>+>>>>+++>+>>>>++>
++>>>>+>>++>+>+++>>>++>>++++++>>+>>++>+>>>>+++>>+++++>>+>+++>>>++>>++>>+>>++>+>+++>>>++>>+++++
++++++++>>+>>++>+>+++>+>+++>>>++>>++++>>+>>++>+>>>>+++>>+++++>>>>++>>>>+>+>++>>+++>+>>>>+++>+>
>>>+++>+>>>>+++>>++>++>+>+++>+>++>++>>>>>>++>+>+++>>>>>+++>>>++>+>+++>+>+>++>>>>>>++>>>+>>>++>
+>>>>+++>+>>>+>>++>+>++++++++++++++++++>>>>+>+>>>+>>++>+>+++>>>++>>++++++++>>+>>++>+>>>>+++>>+
+++++>>>+>++>>+++>+>+>++>+>+++>>>>>+++>>>+>+>>++>+>+++>>>++>>++++++++>>+>>++>+>>>>+++>>++++>>+
>+++>>>>>>++>+>+++>>+>++>>>>+>+>++>+>>>>+++>>+++>>>+[[->>+<<]<+]+++++[->+++++++++<]>.[+]>>[<<+
++++++[->+++++++++<]>-.------------------->-[-<.<+>>]<[+]<+>>>]<<<[-[-[-[>>+<++++++[->+++++<]]
>++++++++++++++<]>+++<]++++++[->+++++++<]>+<<<-[->>>++<<<]>[->>.<<]<<]
		)")
	);
	a_machine.end_eat(); 

	
	cout<<"THE END."<<endl;
	return 0;

#else  
	if(args == 1) 
	{
		/* 当不输入任何参数的时候bf代码和input一次性从stdin获得。
			例1：
./main << EOF
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.
>+.
+++++++..
+++.
>++.
<<+++++++++++++++.>.+++.------.--------.>+.>
<<EOP>>
EOF
			应当输出Hello World!


			例2：
./main<< EOF
,.[,.]
<<EOP>>
,.[,.]
EOF
			应当输出,.[,.]
		 */
		string s;
		cout << "type CODE here (end by <<EOP>> on single line):" << endl;
		while(getline(cin, s)) {
			if (s == "<<EOP>>") break;
			a_machine.eat(s);
		}
		a_machine.INPUT_STREAM.push(&cin);
		cout  << "type INPUT here (end by Ctrl-d):" << endl;
	}
	else if(args == 2) 
	{
		/*
		./main program.bf << END_OF_INPUT
		something blblblblblbl
		blblbllblblblbl
		END_OF_INPUT
		*/
		ifstream is_program(argv[1]);

		string s;
		while(getline(is_program, s)) {
			a_machine.eat(s);
		}
		a_machine.INPUT_STREAM.push(&cin);
 		cout  << "type INPUT here (end by Ctrl-d):" << endl;
	} 
	else {
		/*
		./main program.bf input1.txt input2.txt ... inputN.txt
		*/
		ifstream is_program(argv[1]);

		string s;
		while(getline(is_program, s)) {
			a_machine.eat(s);
		}

		for(int i=2; i<args ; i++){
			a_machine.INPUT_STREAM.push(new ifstream(argv[i]));
		}
		
		a_machine.INPUT_STREAM.push(&cin);
		cout  << "type INPUT here (end by Ctrl-d):" << endl;
	}

	return a_machine.end_eat();
#endif
}
